package com.ternnetwork.workflow.service.impl;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.FormService;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ManagementService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.form.FormProperty;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricProcessInstanceQuery;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.image.ProcessDiagramGenerator;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import com.ternnetwork.commons.model.http.BaseResponse;
import com.ternnetwork.commons.utils.DateUtils;
import com.ternnetwork.workflow.dto.HistoricProcessInstanceQueryDto;
import com.ternnetwork.workflow.enums.BpmsActivityTypeEnum;
import com.ternnetwork.workflow.service.ProcessDefinitionService;
import com.ternnetwork.workflow.service.ProcessInstanceService;
import com.ternnetwork.workflow.utils.UtilMisc;
import com.ternnetwork.workflow.vo.HistoricProcessInstanceVo;







@Service
public class ProcessInstanceServiceImpl implements ProcessInstanceService {
	
	
	@Autowired
	private ManagementService managementService;
	@Autowired
	private IdentityService identityService;
	@Autowired
	private RuntimeService runtimeService;
	
	@Autowired
	private FormService formService;
	@Autowired
	private HistoryService historyService;
	@Autowired
	private RepositoryService repositoryService;
	@Autowired
	private ProcessEngine processEngine;
	
	@Autowired
	private ProcessDefinitionService processDefinitionService;
	
	

	/**
	 * 发起一个流程
	 * identityService.setAuthenticatedUserId(userName) 作用是设置流程的发起者
	 * startEvent 元素的 activiti:initiator="owner" 属性将发起者赋值给 owner
	 * 那么userTask元系的assignee="owner"属性将使此任务的认领者就是流程的发起者
	 * @param userName 发起用户
	 * @param processDefinitionId 流程定义ID
	 * @param oriFormDataMap 表单原始数据
	 * @return
	 */
	@Override
	public BaseResponse startProcessInstanceByKey(String userName,String processDefinitionId,Map<String,String[]> oriFormDataMap){
		
		Map<String, String> formDataMap = convertFormData(oriFormDataMap);
		
		identityService.setAuthenticatedUserId(userName);
		
		//保存表单的数据
		ProcessInstance processInstance=formService.submitStartFormData(processDefinitionId,formDataMap);
		
		BaseResponse baseResponse=new BaseResponse();
		
		baseResponse.setSccucess(processInstance.getBusinessKey());
		
		return baseResponse;
		
	}
	
	
	
	/**
	 * 查找历史经办流程实例
	 * @param pageable
	 * @param dto
	 * @return
	 */
	@Override
	public BaseResponse findByPage(Pageable pageable,HistoricProcessInstanceQueryDto dto) {

		HistoricProcessInstanceQuery q=historyService.createHistoricProcessInstanceQuery();
		
		//可以通过Task参数值来查找流程实例
		//HistoricTaskInstanceQuery hti=historyService.createHistoricTaskInstanceQuery();
		
		
		
		if(!StringUtils.isEmpty(dto.getUserName())){
			q.involvedUser(dto.getUserName());
		}
		if(!StringUtils.isEmpty(dto.getProcessDefinitionId())){
			q.processDefinitionId(dto.getProcessDefinitionId());
		}
		
		if(!StringUtils.isEmpty(dto.getProcessDefinitionName())){
			q.processDefinitionName(dto.getProcessDefinitionName());
		}
		
		
		if(!StringUtils.isEmpty(dto.getStartedBefore())){
			q.startedBefore(DateUtils.getStartDateTime(dto.getStartedBefore()));
		}
		if(!StringUtils.isEmpty(dto.getStartedAfter())){
			q.startedAfter(DateUtils.getEndDateTime(dto.getStartedAfter()));
		}
		
		if(!StringUtils.isEmpty(dto.getFinishedBefore())){
			q.finishedBefore(DateUtils.getStartDateTime(dto.getFinishedBefore()));
		}
		if(!StringUtils.isEmpty(dto.getFinishedAfter())){
			q.finishedAfter(DateUtils.getEndDateTime(dto.getFinishedAfter()));
		}
		if(dto.getFinished()!=null){
			if(dto.getFinished()==true) {
				 q.finished();
			}else {
				  q.unfinished();	
			}
		   
		}
		
		q.orderByProcessInstanceStartTime();
		
		q.desc();
		
        int firstResult=Integer.valueOf(String.valueOf(pageable.getOffset()));
		
		int maxResult=pageable.getPageSize();
		
		long totalElements=q.count();
		
		List<HistoricProcessInstance> list=q.listPage(firstResult,maxResult);
		
		List<HistoricProcessInstanceVo> historicProcessInstanceVoList=convertToHistoricProcessInstanceVo(list);
		
        Map<String,Object> data=new HashMap<String,Object>(10);
		
   		data.put("content", historicProcessInstanceVoList);
		
		data.put("totalElements", totalElements);
		
		BaseResponse baseResponse=new BaseResponse();
			
		baseResponse.setSccucess(data);
			
		return baseResponse;
	}

	
	private List<HistoricProcessInstanceVo> convertToHistoricProcessInstanceVo(List<HistoricProcessInstance> list){
		List<HistoricProcessInstanceVo> retVal=new ArrayList<HistoricProcessInstanceVo>();
		for(HistoricProcessInstance hpi:list){
			HistoricProcessInstanceVo hpivo=new HistoricProcessInstanceVo();
			hpivo.setId(hpi.getId());
			hpivo.setProcessDefinitionId(hpi.getProcessDefinitionId());
			hpivo.setStartUserId(hpi.getStartUserId());
			hpivo.setStartTime(hpi.getStartTime());
			hpivo.setEndTime(hpi.getEndTime());
			ProcessDefinition pd=processDefinitionService.getOne(hpi.getProcessDefinitionId());
			if(pd!=null){
				hpivo.setProcessDefinitionName(pd.getName());
			}
			
		    retVal.add(hpivo);
		}
		return retVal;
	}



	/**
	 * 转换表单数据
	 * @param oriFormDataMap
	 * @return
	 */
	private Map<String, String> convertFormData(Map<String, String[]> oriFormDataMap) {

		Map<String,String> formDataMap=new HashMap<String,String>(10);
		if(oriFormDataMap==null) {
			return formDataMap;
		}
		Set<String> keyset=oriFormDataMap.keySet();
		
		for(String key:keyset){
			String[] values=oriFormDataMap.get(key);
			String value=values[0];
			//排除流程定义ID
			if(!key.equals("processDefinitionId")){
			   formDataMap.put(key,value);
			}
		}
		return formDataMap;
	}
	
	
	/**
	 * 获取启动时的表单参数
	 * @param processDefinitionId
	 * @return
	 */
	@Override
	public BaseResponse getStartFormData(String processDefinitionId) {
		
		List<FormProperty> list=formService.getStartFormData(processDefinitionId).getFormProperties();
		
		BaseResponse baseResponse=new BaseResponse();
		
		baseResponse.setSccucess(list);
		
		return baseResponse;
		
	}
	
	
	
	@Override
	public BaseResponse delete(String id) {
		
		ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
				.processInstanceId(id)
				.singleResult();

		if (processInstance == null) {
            //该流程实例已经完成了
			historyService.deleteHistoricProcessInstance(id);
		} else {
            //该流程实例未结束的，删除顺序不能换
			runtimeService.deleteProcessInstance(id, "");
			
			historyService.deleteHistoricProcessInstance(id);
		}

		BaseResponse baseResponse = new BaseResponse();

		baseResponse.setSccucess();

		return baseResponse;
		
	}
	
	
	/**
	 * 获取流程实例图
	 * @param id
	 * @return
	 */
	@Override
	public InputStream getDiagram(String id) {
       
        // 获取历史流程实例
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult();
                    
        // 获取流程中已经执行的节点，按照执行先后顺序排序
        List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(id).orderByHistoricActivityInstanceId().asc().list();
        
        // 构造已执行的节点ID集合
        List<String> executedActivityIdList = new ArrayList<String>();
        for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
            executedActivityIdList.add(activityInstance.getActivityId());
        }
        
        // 获取bpmnModel
        BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
        // 获取流程已发生流转的线ID集合
        List<String> flowIds = this.getExecutedFlows(bpmnModel, historicActivityInstanceList);
        
        // 使用默认配置获得流程图表生成器，并生成追踪图片字符流
        ProcessDiagramGenerator processDiagramGenerator = processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator();
        
        InputStream imageStream = processDiagramGenerator.generateDiagram(bpmnModel, "png", executedActivityIdList, flowIds, "宋体", "微软雅黑", "黑体", null, 2.0);
       
        return imageStream;
        
	}
	
	private List<String> getExecutedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
        // 流转线ID集合
        List<String> flowIdList = new ArrayList<String>();
        // 全部活动实例
        List<FlowNode> historicFlowNodeList = new LinkedList<FlowNode>();
        // 已完成的历史活动节点
        List<HistoricActivityInstance> finishedActivityInstanceList = new LinkedList<HistoricActivityInstance>();
        for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
            historicFlowNodeList.add((FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true));
            if (historicActivityInstance.getEndTime() != null) {
                finishedActivityInstanceList.add(historicActivityInstance);
            }
        }
        
        // 遍历已完成的活动实例，从每个实例的outgoingFlows中找到已执行的
        FlowNode currentFlowNode = null;
        for (HistoricActivityInstance currentActivityInstance : finishedActivityInstanceList) {
            // 获得当前活动对应的节点信息及outgoingFlows信息
            currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
            List<SequenceFlow> sequenceFlowList = currentFlowNode.getOutgoingFlows();
            
            /**
             * 遍历outgoingFlows并找到已已流转的
             * 满足如下条件认为已已流转：
             * 1.当前节点是并行网关或包含网关，则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
             * 2.当前节点是以上两种类型之外的，通过outgoingFlows查找到的时间最近的流转节点视为有效流转
             */
            FlowNode targetFlowNode = null;
            if (BpmsActivityTypeEnum.PARALLEL_GATEWAY.getType().equals(currentActivityInstance.getActivityType())
                    || BpmsActivityTypeEnum.INCLUSIVE_GATEWAY.getType().equals(currentActivityInstance.getActivityType())) {
                // 遍历历史活动节点，找到匹配Flow目标节点的
                for (SequenceFlow sequenceFlow : sequenceFlowList) {
                    targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true);
                    if (historicFlowNodeList.contains(targetFlowNode)) {
                        flowIdList.add(sequenceFlow.getId());
                    }
                }
            } else {
                List<Map<String, String>> tempMapList = new LinkedList<Map<String,String>>();
                // 遍历历史活动节点，找到匹配Flow目标节点的
                for (SequenceFlow sequenceFlow : sequenceFlowList) {
                    for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
                        if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
                            tempMapList.add(UtilMisc.toMap("flowId", sequenceFlow.getId(), "activityStartTime", String.valueOf(historicActivityInstance.getStartTime().getTime())));
                        }
                    }
                }
                
                // 遍历匹配的集合，取得开始时间最早的一个
                long earliestStamp = 0L;
                String flowId = null;
                for (Map<String, String> map : tempMapList) {
                    long activityStartTime = Long.valueOf(map.get("activityStartTime"));
                    if (earliestStamp == 0 || earliestStamp >= activityStartTime) {
                        earliestStamp = activityStartTime;
                        flowId = map.get("flowId");
                    }
                }
                flowIdList.add(flowId);
            }
        }
        return flowIdList;
    }

	
	

	

}
